package org.an.demo.view;

import android.content.Context;
import android.support.v4.view.ViewConfigurationCompat;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import android.widget.LinearLayout;
import android.widget.Scroller;

/**
 * Created by an on 2017/4/7.<br/>
 */

public class ScrollLinearLayout extends LinearLayout {
  private Scroller mScroller;
  private GestureDetector mGestureDetector;
  /**
   * 判定为拖动的最小移动像素数
   */
  private int mTouchSlop;

  /**
   * 界面可滚动的左边界
   */
  private int topBorder;

  /**
   * 界面可滚动的右边界
   */
  private int bottomBorder;

  public ScrollLinearLayout(Context context) {
    super(context);
    init();
  }

  public ScrollLinearLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
    init();
  }

  class MyGestureDetector extends GestureDetector.SimpleOnGestureListener {
    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
      mScroller.startScroll(0, (int) mLastY, 0, (int) distanceX);
      invalidate();
      return false;
    }

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
      System.out.println("onFling velocityY = " + velocityY);
      final int maxY = 0x7FFFFFFF;
      final int minY = -maxY;
      mScroller.fling(0, (int) mLastY, 0, (int) -velocityY, 0, 0, minY, maxY);
      invalidate();
      return false;
    }
  }

  private void init() {
    setOrientation(VERTICAL);
    mScroller = new Scroller(getContext());
    mGestureDetector = new GestureDetector(getContext(), new MyGestureDetector());
    ViewConfiguration configuration = ViewConfiguration.get(getContext());
    mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);
  }

  @Override public boolean onInterceptTouchEvent(MotionEvent ev) {
    switch (ev.getAction()) {
      case MotionEvent.ACTION_DOWN:
        mLastY = ev.getY();
        mLastDownY = mLastY;
        return false;
      case MotionEvent.ACTION_MOVE:
        // 当手指拖动值大于TouchSlop值时，认为应该进行滚动，拦截子控件的事件
        // 这里不能直接return true，否则子view无法响应点击事件（点击事件是在up的时候触发的）
        // 如果我们把mTouchSlop调整的很大，那么分析一下
        // 1.down事件onInterceptTouchEvent不拦截，子view消费，然后move事件，子view消费（由于这里很大），
        // 如果滑动的距离没有超过TouchSlop，不拦截所以move事件都是子view处理的，
        // 只要没有超过TouchSlop，onInterceptTouchEvent一直不拦截，move事件都会经onInterceptTouchEvent
        // 最后up事件，子view响应点击事件
        // 如果某次move超过了，被拦截，则viewgroup onTouchEvent处理move事件

        mLastY = ev.getY();
        System.out.println("onInterceptTouchEvent move = " + mLastY);
        float diff = Math.abs(mLastY - mLastDownY);
        if (diff > mTouchSlop) {
          return true;
        }
        return false;
    }
    return super.onInterceptTouchEvent(ev);
  }

  private float mLastY;
  private float mLastDownY;

  @Override public boolean onTouchEvent(MotionEvent event) {
    mLastY = event.getY();
    mGestureDetector.onTouchEvent(event);

   /* switch (event.getAction()) {
      // 如果down事件传递到这里，也就意味着子view没有消费，那么这里必须消费，否则后续事件就不会再处理，也就不能move了
      case MotionEvent.ACTION_DOWN:
        return true;
      case MotionEvent.ACTION_MOVE:
        float y = event.getY();
        int scrolledY = (int) (mLastY - y);
        mLastY = y;
        // 如果滑动距离超过边界就scrollTo到边界（scrollTo内部处理已经在边界的话就不会走了）
        if (getScrollY() + scrolledY < topBorder) {
          scrollTo(0, topBorder);
          return true;
        } else if (getScrollY() + scrolledY > bottomBorder - getHeight()) {
          // 最多只能滑到 rightBorder - getHeight()
          scrollTo(0, bottomBorder - getHeight());
          return true;
        }
        scrollBy(0, scrolledY);
        break;
    }*/
    return super.onTouchEvent(event);
  }

  /**
   * 由父视图调用用来请求子视图根据偏移值 mScrollX,mScrollY重新绘制
   * 在draw的时候调用，scrollTo 和scrollBy都会引起invalidate
   */
  @Override public void computeScroll() {
    // 这里判断动画没有结束（mScroller.computeScrollOffset()返回true），就读取当前的值，然后scrollTo到该值
    // 其实说白了就是利用scroller的动画插值器的变化（调用mScroller.startScroll），
    // 在view重绘（父视图调用用来请求子视图根据偏移值 mScrollX,mScrollY重新绘制）的时候（会调用computeScroll方法）
    // 滚动到scroller的位置
    if (mScroller.computeScrollOffset()) {
      scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
      // 这里必须调用invalidate，因为虽然scrollTo内部会调用invalidate，
      // 但是如果scrollTo 的位置与当前位置一样就会导致导致不会再invalidate，
      // 导致无法在回调computeScroll，所以就没法动了
      //invalidate();
    }
  }
}
